using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using GuavaData;
using GuavaLib;

namespace GuavaLib
{
    public partial class FindingPicker : UserControl, Guava, GuavaLaunchBase
    {
        string gname;
        string[] forms;
        Assembly asm;
        DataView dv;

        public FindingPicker()
        {
            InitializeComponent();
            asm = Assembly.GetCallingAssembly();
        }

        public string[] Forms
        {
            get { return forms; }
            set
            {
                forms = value;
            }
        }

        #region Guava Members

        [Browsable(false)]
        public LaunchType LType
        {
            get { return LaunchType.Single; }
        }

        [Browsable(false)]
        public ControlType CType
        {
            get { return ControlType.Entity; }
        }

        public string GText
        {
            get { return this.lblText.Text; }
            set { this.lblText.Text = value; }
        }

        public string GName
        {
            get
            {
                if (gname == null) return this.Name;
                return gname;
            }
            set
            {
                gname = value;
            }
        }

        [Browsable(false)]
        public Point GLocation
        {
            get { return this.Location; }
        }

        [Browsable(false)]
        public Size GSize
        {
            get { return this.Size; }
        }

        [Browsable(false)]
        public List<Guava> GComponents
        {
            get
            {
                List<Guava> lg = new List<Guava>();
                lg.Add(new GEmpty("Form", new GuavaData.Domain.Enumerated(forms)));
                lg.Add(new GEmpty("Created", new GuavaData.Domain.DateTime()));
                
                foreach (string s in this.forms)
                {
                    Type t = GLaunchClass.RetrieveTypeFromFile(s, this.asm);
                    if (t == null) continue;
                    ConstructorInfo ci = t.GetConstructor(new Type[] { });
                    Object o = ci.Invoke(new object[] { });
                    lg.Add(o as Guava);
                }
                
                return lg;
            }
        }

        public void GBind(string table, int i)
        {
            // Retrieve the ID from the parent form
            GuavaForm gf = this.ParentForm as GuavaForm;
            Guid gid = gf.ID;
            gf.PersistSignal += new GuavaData.SignalToPersist(gf_PersistSignal);

            // Fetch the appropriate rows
            AppData.FetchRowsByFK(this.GName, gid);
            DataTable targetTable = AppData.ds.Tables[this.GName];

            // Accept loading of rows into memory, just in case
            targetTable.AcceptChanges();

            // Create a view of the destination table that only has the current ID's
            dv = new DataView(targetTable, "fk='" + gid.ToString() + "'", "Created", DataViewRowState.CurrentRows);

            BuildList();
            this.cmbItems.Sorted = true;

            return;
        }

        [Browsable(false)]
        public string GDefault
        {
            get { return null; }
        }

        [Browsable(false)]
        public string GToolTip
        {
            get
            {
                GuavaForm gf = this.ParentForm as GuavaForm;
                if (gf == null) return null;

                // Pull the ToolTip info
                ToolTip t = gf.GToolTipProvider;
                if (t == null) return null;

                // Fetch the tooltip and return it
                return t.GetToolTip(this);

            }
        }

        [Browsable(false)]
        public GuavaData.Domain.Domain GDomain
        {
            get { return new GuavaData.Domain.UniqueIdentifier(); }
        }

        public Guava this[string s]
        {
            get
            {
                foreach (Guava g in this.GComponents) if (g.GName == s) return g;
                return null;
            }
        }

        #endregion

        private void BuildList()
        {
            // Build the list of combobox items
            List<object> lo = new List<object>();
            foreach (System.Data.DataRowView dr in dv)
            {
                lo.Add(new GuavaData.Domain.AnnotatedGuid((Guid)dr["id"], String.Concat(dr["Form"].ToString(), ":", dr["Created"].ToString()), dr));
            }

            GuavaData.Domain.AnnotatedGuid ag = this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid;
            this.cmbItems.Items.Clear();
            this.cmbItems.Items.AddRange(lo.ToArray());
            this.cmbItems.Refresh();
            if (ag != null) SetObject(ag.Object);
        }

        #region GuavaLaunchBase Members

        public void SetObject(Guid id)
        {
            foreach (object o in this.cmbItems.Items)
            {
                GuavaData.Domain.AnnotatedGuid ag = o as GuavaData.Domain.AnnotatedGuid;
                if (ag.Object.Equals(id))
                {
                    this.cmbItems.SelectedItem = o;
                    return;
                }
            }

            this.cmbItems.SelectedIndex = -1;
            this.cmbItems.Refresh();
        }

        public void PrepareStackFrame()
        {
            // Build the seed stack frame call list
            GuavaForm gForm = this.ParentForm as GuavaForm;
            GSeed gsTemp = gForm.GS;
            GSeedFrame gsfTemp = gsTemp.Peek();

            // Create a list of controls
            Control p = this;
            Control q = p.Parent;
            List<string> ls = new List<string>();
            ls.Add(this.GName);

            // Follow parents up the chain to the root form
            while (!(q is GuavaForm))
            {
                if (!(q is Guava))
                {
                    Guava r = q.Parent as Guava;
                    if (r == null) throw new Exception("Found two non-guava objects nested together in the same tree for marking.");
                    bool bfound = false;
                    foreach (Guava g in r.GComponents)
                    {
                        if (g.GComponents.Contains(p as Guava))
                        {
                            ls.Insert(0, g.GName);
                            bfound = true;
                            break;
                        }
                    }
                    if (!bfound) throw new Exception("Could not match a guava object for creating a seed for marking.");
                }
                else ls.Insert(0, (q as Guava).GName);
                p = p.Parent;
                q = q.Parent;
            }
            gsfTemp.controls = ls;
        }

        public DialogResult LaunchForm(Guid gid, string form, bool bNew)
        {
            DialogResult d;
            
            Type t = GLaunchClass.RetrieveTypeFromFile(form, this.asm);
            if (t == null) return DialogResult.Cancel;

            ConstructorInfo ci = t.GetConstructor(new Type[] { typeof(Guid), typeof(Guid), typeof(bool), typeof(GSeed) });
            GForm o = ci.Invoke(new object[] { gid, gid, bNew, (this.ParentForm as GuavaForm).GS }) as GForm;
            this.PrepareStackFrame();
            d = o.ShowDialog();

            // When done, refresh the items
            BuildList();
            this.cmbItems.Refresh();
            return d;
        }

        #endregion

        private void btnNew_Click(object sender, EventArgs e)
        {
            FindingPickerPicker fpp = new FindingPickerPicker();
            fpp.Findings = this.forms;
            DialogResult dres = fpp.ShowDialog();
            if (dres == DialogResult.Cancel) return;

            // If we get here, we need a new item
            Guid gid = Guid.NewGuid();
            string f = fpp.SelectedFinding;

            // Create a new row for the procedure table
            DataRowView drnew = dv.AddNew();
            drnew["id"] = gid;
            drnew["fk"] = (this.ParentForm as GuavaForm).ID;
            drnew["Form"] = f;
            drnew["Created"] = DateTime.Now;
            drnew.EndEdit();
            AppData.CommitRow(dv.Table.TableName, gid, DMLType.INSERT);
            dv.Table.AcceptChanges();

            dres = LaunchForm(gid, f, true);
            if (dres == DialogResult.Cancel)
            {
                drnew.Delete();
                AppData.DeleteRow(dv.Table.TableName, gid);
                dv.Table.AcceptChanges();
                return;
            }

            BuildList();
            SetObject(gid);
        }

        private void btnEdit_Click(object sender, EventArgs e)
        {
            if (this.cmbItems.Items.Count <= 0) return;
            if (this.cmbItems.SelectedIndex < 0) return;
            DataRowView drv = (this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid).RowData;
            Guid gid = (Guid)drv["id"];
            string f = drv["Form"].ToString();
            LaunchForm(gid, f, false);
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (this.cmbItems.Items.Count <= 0) return;
            if (this.cmbItems.SelectedIndex < 0) return;

            DataRowView drv = (this.cmbItems.SelectedItem as GuavaData.Domain.AnnotatedGuid).RowData;

            // Need to delete the row in this table, and also in the target table
            // Get the target table's name
            Type t = GLaunchClass.RetrieveTypeFromFile(drv["Form"].ToString(), asm);
            ConstructorInfo ci = t.GetConstructor(new Type[] { });
            Guava o = ci.Invoke(new object[] { }) as Guava;
            string targetTable = o.GName;

            // Delete the appropriate row in the target table
            AppData.DeleteRow(targetTable, (Guid)drv["id"]);

            // Delete the row in the current table
            AppData.DeleteRow(this.gname, (Guid)drv["id"]);

            // Accept changes
            AppData.ds.Tables[targetTable].AcceptChanges();
            BuildList();
        }

        void gf_PersistSignal(object sender, PersistSignalEventArgs e)
        {
            if (e.bPersist) PersistChanges();
            else dv.Table.RejectChanges();
        }

        private void PersistChanges()
        {
            // Purge deleted rows
            dv.RowStateFilter = DataViewRowState.Deleted;
            foreach (DataRowView drv in dv)
                AppData.DeleteRow(dv.Table.TableName, (Guid)drv["id"]);

            // Add new rows
            dv.RowStateFilter = DataViewRowState.Added;
            foreach (DataRowView drv in dv)
                AppData.CommitRow(dv.Table.TableName, (Guid)drv["id"], DMLType.INSERT);

            // Finish
            dv.RowStateFilter = DataViewRowState.CurrentRows;
            dv.Table.AcceptChanges();
        }
    }
}
